package com.essence.grabiec104.grab.service;

import com.essence.common.model.BytesFrame;
import com.essence.common.model.Packet;
import com.essence.common.model.Substation;
import com.essence.grabiec104.grab.dao.AlarmDao;
import com.essence.grabiec104.grab.dao.GrabberDao;
import com.essence.grabiec104.grab.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import static com.essence.grabiec104.grab.model.TimeRange.CalculateMonthlyRanges;

@Service
@Slf4j
public class AlarmService {
    @Autowired
    private AlarmDao alarmDao;

    @Autowired
    private GrabberDao grabberDao;

    /**
     * 获取所有变电站信息
     * 如果缓存为空，则从数据库加载
     *
     * @return 变电站信息列表
     */
    @Cacheable("substationInfos")
    public List<Substation> getAllSubstation() {
        return alarmDao.getAllSubstation();
    }

    /**
     * 获取所有变电站信息，并构建场站组
     */
    @Cacheable("substationGroup")
    public List<Substation> getSubstationGroup() {
        List<Substation> stationGroup = new ArrayList<>();
        List<Substation> subs = alarmDao.getAllSubstation();
        for (Substation sub : subs) {
            if (!sub.isRtu()) {
                stationGroup.add(sub);
            }
        }
        for (Substation sub : subs) {
            if (sub.isRtu()) {
                for (Substation state : stationGroup) {
                    if (state.getId() == sub.getPid()) {
                        state.getSlaveStation().add(sub);
                    }
                }
            }
        }
        return stationGroup;
    }

    /**
     * 添加变电站
     * 添加成功后更新缓存
     *
     * @param substation 变电站信息
     * @return 变电站ID
     */
    @Transactional
    @CacheEvict(value = {"substationInfos", "substationGroup"}, allEntries = true)
    public long addSubstation(Substation substation) {
        substation.setCreatedAt(System.currentTimeMillis());
        substation.setUpdatedAt(System.currentTimeMillis());
        alarmDao.addSubstation(substation);
        return substation.getId();
    }

    /**
     * 更新变电站信息
     * 更新成功后更新缓存
     *
     * @param substation 变电站信息
     * @return 更新结果
     */
    @Transactional
    @CacheEvict(value = {"substationInfos", "substationGroup"}, allEntries = true)
    public int updateSubstation(Substation substation) {
        substation.setUpdatedAt(System.currentTimeMillis());
        int result = alarmDao.updateSubstation(substation);
        return result;
    }

    /**
     * 删除变电站
     * 删除成功后更新缓存
     *
     * @param substation 变电站信息
     * @return 变电站ID
     */
    @Transactional
    @CacheEvict(value = {"substationInfos", "substationGroup"}, allEntries = true)
    public long deleteSubstation(Substation substation) {
        int result = alarmDao.deleteSubstation(substation);
        alarmDao.deleteAlarmRuleByStateId(substation.getId());
        return substation.getId();
    }

    @Cacheable("alarmRules")
    public List<AlarmRule> getAllAlarmRule() {
        return alarmDao.selectAllAlarmRule();
    }

    @Transactional
    public int saveCorrelation(List<CorrelationPointQuery> queries) {
        alarmDao.emptyPointRelation();
        for (CorrelationPointQuery query : queries) {
            alarmDao.insertPointRelation(query);
        }
        return 1;
    }

    @Transactional
    public CycleQuery selectControlInstructionCycle(int cycle) {
        CycleQuery cycleQuery = new CycleQuery();
        List<TimeRange> timeRanges = CalculateMonthlyRanges(cycle);
        for (TimeRange timeRange : timeRanges) {
            int ctrlPacketNum = grabberDao.selectPacketsByTimeAndTieIds(timeRange.getStartTime(), timeRange.getEndTime(), List.of(45, 46, 47, 58, 59, 60));
            int setPacketNum = grabberDao.selectPacketsByTimeAndTieIds(timeRange.getStartTime(), timeRange.getEndTime(), List.of(48, 49, 50, 61, 62, 63));
            cycleQuery.addCategory(timeRange.getMonthDesc());
            cycleQuery.pushSetValue(timeRange.getMonthDesc(), ctrlPacketNum, setPacketNum);
        }
        return cycleQuery;
    }

    public List<YKComparison.YKComparisonItem> selectYKComparison(String host, long timestamp) {
        List<BytesFrame> bytesFrames = alarmDao.selectControlFrameBySubstationId(host, timestamp);
        List<YKComparison.YKComparisonItem> ykComparisonItems = new ArrayList<>();
        for (BytesFrame bytesFrame : bytesFrames) {
            YKComparison.YKComparisonItem item = new YKComparison.YKComparisonItem();
            Packet pkt = bytesFrame.getAndAnalysePacket();
            int asduAddr = pkt.getAsduAddressInt();
            item.setCommonAddress(asduAddr);
            item.setTimestamp(bytesFrame.getTimestamp());
            item.setTieIdentification(pkt.getTieIdentification().getDescription());
            item.setTieIdentificationIndex(pkt.getTieIdentification().getValue());
            item.setCause(pkt.getCot().getCause().getDescription());
            item.setCauseIndex(pkt.getCot().getCause().getCode());
            if (!pkt.getDigitalSignals().isEmpty()) {
                Packet.DigitalSignals ds = pkt.getDigitalSignals().get(0);
                item.setPointAddress(ds.getInfoAdr());
                item.setValue(ds.getSpi());
                item.setIv(ds.getIv());
                item.setDirection(bytesFrame.getDirection() == BytesFrame.DirectionEnum.Upstream ? 0 : 1);
            }
            ykComparisonItems.add(item);

        }
        return ykComparisonItems;
    }

    /**
     * 更新告警规则
     * 1. 将所有告警规则的enabled置为false
     * 2. 根据条件更新或插入新的告警规则
     * 3. 删除所有enabled为false的规则及其相关告警记录
     *
     * @param queries 告警规则列表
     */
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "alarmRules", allEntries = true)
    public void updateAlarmRules(List<AlarmRuleQuery> queries) {
        // 1. 将所有告警规则的enabled置为false
        alarmDao.disableAllAlarmRules();

        // 2. 更新或插入新的告警规则
        for (AlarmRuleQuery query : queries) {
            AlarmRuleQuery existingRule = alarmDao.selectAlarmRuleByCondition(query);
            if (existingRule != null) {
                // 更新现有规则
                alarmDao.updateAlarmRuleByCondition(query);
            } else {
                // 插入新规则
                query.setEnabled(true);
                alarmDao.addAlarmRules(List.of(query));
            }
        }

        // 3. 删除所有enabled为false的规则及其相关告警记录
        List<Long> disabledRuleIds = alarmDao.selectDisabledAlarmRuleIds();
        if (!disabledRuleIds.isEmpty()) {
            // 分批处理，每批最多100条
            int batchSize = 100;
            for (int i = 0; i < disabledRuleIds.size(); i += batchSize) {
                int endIndex = Math.min(i + batchSize, disabledRuleIds.size());
                List<Long> batchIds = disabledRuleIds.subList(i, endIndex);

                // 先删除告警记录
                alarmDao.deleteAlarmRecordsByRuleIds(batchIds);
                // 再删除告警规则
                alarmDao.deleteAlarmRulesByIds(batchIds);

                log.info("已删除第{}批告警规则，本批数量：{}", (i / batchSize + 1), batchIds.size());
            }
        }
    }

    public List<Integer> last30DaysAlarmCount() {
        LinkedList<Integer> counts = new LinkedList<>();
        for (int i = 0; i < 30; i++) {
            LocalDate startDate = LocalDate.now().minusDays(i);
            Instant startOfYesterdayInstant = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
            long startTimestamp = startOfYesterdayInstant.toEpochMilli();
            long endTimestamp = startOfYesterdayInstant.plus(1, ChronoUnit.DAYS).minusNanos(1).toEpochMilli();
            int cnt = alarmDao.sumAlarmRecordByCycle(startTimestamp, endTimestamp);
            // 向前添加
            counts.addFirst(cnt);
        }
        return counts;
    }

    public List<Integer> last30daysInstructionCount() {
        LinkedList<Integer> counts = new LinkedList<>();
        for (int i = 0; i < 30; i++) {
            LocalDate startDate = LocalDate.now().minusDays(i);
            Instant startOfYesterdayInstant = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
            long startTimestamp = startOfYesterdayInstant.toEpochMilli();
            long endTimestamp = startOfYesterdayInstant.plus(1, ChronoUnit.DAYS).minusNanos(1).toEpochMilli();
            int ctrlPacketNum = grabberDao.selectPacketsByTimeAndTieIds(startTimestamp, endTimestamp, List.of(45, 46, 47, 58, 59, 60));
            int setPacketNum = grabberDao.selectPacketsByTimeAndTieIds(startTimestamp, endTimestamp, List.of(48, 49, 50, 61, 62, 63));
            counts.addFirst(ctrlPacketNum + setPacketNum);
        }
        return counts;
    }
}